// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v4"
	"github.com/rookie-ninja/rk-boot/v2"
	"github.com/rookie-ninja/rk-entry/v2/entry"
	"github.com/rookie-ninja/rk-gin/v2/boot"
	"github.com/rookie-ninja/rk-gin/v2/middleware/context"
	"go.uber.org/zap"
	"io/ioutil"
	"net/http"
	"time"
)

// @title Swagger Example API
// @version 1.0
// @description This is a sample rk-demo server.
// @termsOfService http://swagger.io/terms/

// @securityDefinitions.basic BasicAuth

// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io

// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
func main() {
	// Create a new boot instance.
	boot := rkboot.NewBoot(rkboot.WithBootConfigPath("client.yaml", nil))

	// Register handler
	entry := rkgin.GetGinEntry("rk-client")
	//增加自定义中间件
	entry.AddMiddleware(AccessLogHandler())
	//路由参数
	entry.Router.GET("/v1/greeter", Greeter)
	entry.Router.GET("/v1/test", TestAcion)
	entry.Router.GET("/v1/login", Login)
	entry.Router.POST("/v1/whoami", WhoAmI)

	// Bootstrap
	boot.Bootstrap(context.Background())

	boot.WaitForShutdownSig(context.Background())
}

// AccessLogHandler
//  AccessLogHandler
//  @Description: 记录请求和响应参数
//  @return gin.HandlerFunc
//
func AccessLogHandler() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		event := rkginctx.GetEvent(ctx)
		body, _ := ioutil.ReadAll(ctx.Request.Body)
		event.AddPayloads(zap.String("request body", string(body)))
		ctx.Request.Body = ioutil.NopCloser(bytes.NewReader(body))
		blw := &CustomResponseWriter{body: bytes.NewBufferString(""), ResponseWriter: ctx.Writer}
		ctx.Writer = blw
		ctx.Next()

		event.AddPair("response body", blw.body.String())
		//event.AddPair("body", fmt.Sprintf("url=%s, status=%d, resp=%s", ctx.Request.URL, ctx.Writer.Status(), blw.body.String()))
	}
}

type CustomResponseWriter struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

func (w CustomResponseWriter) Write(b []byte) (int, error) {
	w.body.Write(b)
	return w.ResponseWriter.Write(b)
}

func (w CustomResponseWriter) WriteString(s string) (int, error) {
	w.body.WriteString(s)
	return w.ResponseWriter.WriteString(s)
}

// TestAcion TestAcion handler
// @Summary TestAcion
// @Id 2
// @Tags Hello
// @version 1.0
// @Param name query string true "name"
// @produce application/json
// @Success 200 {object} GreeterResponse
// @Router /v1/test [get]
func TestAcion(ctx *gin.Context) {
	ctx.JSON(http.StatusOK, &GreeterResponse{
		Message: fmt.Sprintf("Hello %s!", ctx.Query("name")),
	})
}

// Greeter handler
// @Summary Greeter
// @Id 1
// @Tags Hello
// @version 1.0
// @Param name query string true "name"
// @produce application/json
// @Success 200 {object} GreeterResponse
// @Router /v1/greeter [get]
func Greeter(ctx *gin.Context) {
	client := http.DefaultClient
	// Construct request point to Server B
	req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "http://localhost:8088/v1/greeter?name=testing123456789", nil)

	// Inject parent trace info into request header
	rkginctx.InjectSpanToHttpRequest(ctx, req)

	// Send request to Server B
	client.Do(req)

	ctx.JSON(http.StatusOK, "Hello!")
}

type GreeterResponse struct {
	Message string
}

// CustomClaims defines JWT claims
type CustomClaims struct {
	UserName string `json:"uname"`
	jwt.RegisteredClaims
}

// Login handler
// @Summary Login
// @Id 4
// @Tags JWT
// @version 1.0
// @Security  JWT
// @Param name query string true "name"
// @produce application/json
// @Router /v1/login [get]
func Login(ctx *gin.Context) {
	// Simply generate JWT token from user provided name for demo
	userName := ctx.Query("name")

	now := time.Now()
	claims := CustomClaims{
		UserName: userName,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(now.Add(3600 * time.Minute)),
			IssuedAt:  jwt.NewNumericDate(now),
			NotBefore: jwt.NewNumericDate(now),
			Issuer:    "rk-boot",
		},
	}

	// By default, JWT middleware will create a new SignerEntry with the same name of Gin Entry
	// default signer entry will use symmetric algorithm (HS256) with token of (rk jwt key)
	// refer rkmidjwt.NewOptionSet
	signerEntry := rkentry.GlobalAppCtx.GetSignerJwtEntry("rk-demo")
	if signerEntry != nil {
		res, _ := signerEntry.SignJwt(claims)
		ctx.JSON(http.StatusOK, map[string]string{
			"JwtToken": res,
		})
	} else {
		ctx.JSON(http.StatusNotFound, &GreeterResponse{
			Message: fmt.Sprintf("NOT FIND %s!", ctx.Query("name")),
		})
	}

}

// WhoAmI handler
// @Summary WhoAmI
// @Id 3
// @Tags JWT
// @version 1.0
// @Security  JWT
// @produce application/json
// @Router /v1/whoami [get]
func WhoAmI(ctx *gin.Context) {
	// 1: get JWT token from context which injected into context by middleware
	token := rkginctx.GetJwtToken(ctx)

	event := rkginctx.GetEvent(ctx)

	// convert claim to custom claim
	claims := &CustomClaims{}
	bytes, _ := json.Marshal(token.Claims)
	json.Unmarshal(bytes, claims)
	//time.Sleep(10 * time.Second)
	ctx.JSON(http.StatusOK, map[string]string{
		"Message":   fmt.Sprintf("Your name is %s", claims.UserName),
		"ExpiresAt": fmt.Sprintf("Your ExpiresAt is %s", claims.ExpiresAt),
		"Issuer":    fmt.Sprintf("Your Issuer is %s", claims.Issuer),
		"requestId": event.GetEventId(),
	})
}
